# Copyright (C) 2012 Thiago Chaves de Oliveira Horta <shundread@gmail.com>
#
# This file is part of Kapybara, and is provided to you as is under the terms
# and conditions of the GPL v3 license. The license should have been provided to
# you with this package via the file gpl-3.0.txt, but if not you can see it
# online at https://www.gnu.org/licenses/gpl-3.0.html

import sqlite3
import codecs

from language import *

# Ideal parameter order:
#
# X_Y(X, Y [, language])
# X_name(X [, language])

RowDelimiter = '\t'

Debug = 1

# Utility functions

def _id(x):
    if x == None:
        return 0
    if isinstance(x, int):
        return x
    return x.id

# Classes

class Database(object):
    '''This class is the base class to all Kapybara databases. The database is the
    class that handles the calls on kapybara's language classes.'''
    def __init__(self):
        pass

    # Language
    def languages(self):
        '''Returns a list of languages in the database.'''
        pass

    def language_name(self, language, target_language=None):
        '''Returns the name of a given language. The name can be translated to
        a target language if the second parameter is passed.'''
        pass
        
    # ExpressionClass queries
    def expression_classes(self, language=None):
        '''Returns a list of expression classes. The search can be restricted to
        expression classes present in a given language.'''
        pass

    def expression_class_name(self, eclass, language=None):
        '''Returns the name of an expression class.'''
        pass

    # Function queries
    def functions(self, language=None):
        '''Returns a list of languages in a given language.'''
        pass

    def function_name(self, function, language=None):
        '''Returns the name of a function.'''
        pass

    def function_evaluate(self, function, parameter):
        '''Evaluates the function and returns the result expression or None, if
        the function is not defined for that value.'''
        pass

    # Expression queries
    def expressions(self, language=None):
        '''Returns the list of expressions in a given language.'''
        pass

    def expression(self, expression_id):
        '''Returns the expression indexed by the id in the given language.'''
        pass

###########################################

class FileDatabase(Database):
    '''This class interprets databases stored in the .kap format.'''
    def __init__(self, filepath):
        Database.__init__(self)
        self.path = filepath

        # In name dicts order of parameters is dict[thing_id][language_id]
        self._languages = {}
        self._language_names = {}
        self._language_functions = {}
        self._language_expression_classes = {}
        self._language_expressions = {}

        self._functions = {}
        self._function_names = {}
        self._function_values = {} #[function_id][expression_id][language_id]

        self._expression_classes = {}
        self._expression_class_names = {}

        self._expressions = {}
        self._expression_names = {}

        self._read_file(filepath)

    def _read_file(self, filepath):
        f_in = codecs.open(filepath, 'r', 'utf-8')
        text = f_in.read()
        f_in.close()

        sections = {}
        sections_raw = [chunk.strip() for chunk in text.split('---')]
        section_chunks = [chunk.split('\n') for chunk in sections_raw]
        for chunk in section_chunks:
            lines = [line.strip() for line in chunk]
            section_name = lines[0]
            rows = [line.split(RowDelimiter) for line in lines[1:]]
            if section_name == 'Languages':
                self._parse_languages(rows)
            elif section_name == 'Expression Classes':
                self._parse_expression_classes(rows)
            elif section_name == 'Functions':
                self._parse_functions(rows)
            elif section_name == 'Expressions':
                self._parse_expressions(rows)
            else:
                (language_name, function_name) = section_name.split(':')[0:2]
                self._parse_language_function(language_name, function_name, rows)

    def _parse_languages(self, rows):
        for language_id, row in enumerate(rows):
            self._languages[language_id] = Language(self, language_id)
            self._language_names[language_id] = {}
            self._language_functions[language_id] = {}
            self._language_expression_classes[language_id] = {}
            self._language_expressions[language_id] = {}
            for lang, name in enumerate(row):
                self._language_names[language_id][lang] = name

    def _parse_expression_classes(self, rows):
        for ec_id, row in enumerate(rows):
            self._expression_classes[ec_id] = ExpressionClass(self, ec_id)
            self._expression_class_names[ec_id] = {}
            for language_id, name in enumerate(row):
                self._expression_class_names[ec_id][language_id] = name

    def _parse_functions(self, rows):
        for function_id, row in enumerate(rows):
            self._functions[function_id] = Function(self, function_id)
            self._function_names[function_id] = {}
            self._function_values[function_id] = {}
            if Debug:
                print "Registering function", function_id, str(row)
            for language_id, name in enumerate(row):
                self._function_names[function_id][language_id] = name

    def _parse_expressions(self, rows):
        for expression_id, row in enumerate(rows):
            self._expressions[expression_id] = {}

            # Grab class id
            ec_name = row[0]
            ec_id = self._grab_eclass_id_by_name(ec_name)

            self._expressions[expression_id] = Expression(self, ec_id,
                expression_id)
            self._expression_names[expression_id] = {}
            for func_id in self._functions.keys():
                self._function_values[func_id][expression_id] = {}
            for language_id, name in enumerate(row[1:]):
                if name:
                    self._expression_names[expression_id][language_id] = name
                    self._language_expressions[language_id][expression_id] = \
                        self._expressions[expression_id]
                    self._language_expression_classes[language_id][ec_id] = \
                        self._expression_classes[ec_id]

    def _parse_language_function(self, language_name, function_name, rows):
        lang_id = self._grab_language_id_by_name(language_name)
        func_id = self._grab_function_id_by_name(function_name)
        self._language_functions[lang_id][func_id] = self._functions[func_id]

        for row in rows:
            ec_id = self._grab_eclass_id_by_name(row[0])
            name = row[1]
            values = row[2:]
            e_id = self._grab_expression_by_name(name, ec_id, lang_id)
            if not self._function_values[func_id].has_key(e_id):
                self._function_values[func_id][e_id] = {}
            self._function_values[func_id][e_id][lang_id] = values

    def _grab_eclass_id_by_name(self, eclass_name):
        for eclass_id, eclass_names in self._expression_class_names.iteritems():
            if eclass_name == eclass_names[0]:
                return eclass_id

    def _grab_function_id_by_name(self, function_name):
        for function_id, function_names in self._function_names.iteritems():
            if function_name == function_names[0]:
                return function_id

    def _grab_language_id_by_name(self, language_name):
        for language_id, language_names in self._language_names.iteritems():
            if language_name == language_names[0]:
                return language_id

    def _grab_expression_by_name(self, expression_name, eclass_id, language_id):
        for e_id, e_names in self._expression_names.iteritems():
            if self._expressions[e_id].eclass_id == eclass_id:
                if expression_name == e_names[language_id]:
                    return e_id

    # Language
    def languages(self):
        print "Returning languages"
        return self._languages

    def language_name(self, language, target_language=None):
        language = _id(language)
        target_language = _id(target_language)
        return self._language_names[language][target_language]
        
    # ExpressionClass queries
    def expression_classes(self, language=None):
        if language != None:
            language = _id(language)
            return self._language_expression_classes[language]
        return self._expression_classes

    def expression_class_name(self, eclass, language=None):
        eclass = _id(eclass)
        language = _id(language)
        return self._expression_class_names[eclass][language]

    # Function queries
    def functions(self, language=None, expression=None):
        if language != None:
            language = _id(language)
            search_dict = self._functions
        else:
            search_dict = self._language_functions[language]

        if expression != None:
            result = {}
            for f_id, f in search_dict.iteritems():
                if f.defined_for(expression):
                    result[f_id] = f
            return result

        return search_dict

    def function_name(self, function, language=None):
        function = _id(function)
        language = _id(language)
        return self._function_names[function][language]

    def function_evaluate(self, function, parameter, language):
        function = _id(function)
        parameter = _id(parameter)
        language = _id(language)
        if not self._function_values.has_key(function):
            return None
        if not self._function_values[function].has_key(parameter):
            return None
        if not self._function_values[function][parameter].has_key(language):
            return None
        return self._function_values[function][parameter][language]

    # Expression queries
    def expressions(self, language=None, eclass=None):
        if language != None:
            language = _id(language)
            unfiltered_expressions = self._language_expressions[language]
        else:
            unfiltered_expressions = self._expressions

        if eclass == None:
            return unfiltered_expressions
        eclass = _id(eclass)
        filtered_expressions = {}
        for e_id, expression in unfiltered_expressions.iteritems():
            if _id(expression.eclass_id) == eclass:
                filtered_expressions[e_id] = expression
        return filtered_expressions

    def expression_name(self, expression, language=None):
        expression = _id(expression)
        language = _id(language)
        return self._expression_names[expression][language]

    def __str__(self):
        return 'FileDatabase("{0}")'.format(self.path)

    def __repr__(self):
        return 'FileDatabase("{0}")'.format(self.path)

###########################################

class SqlDatabase(Database):
    def __init__(self, database):
        self.database = database

    def _query(self, sql_statement, *args):
        if Debug:
            print "Running query:"
            print sql_statement
            print
            return [0]

        connection = sqlite3.connect(self.database)
        cursor = connection.cursor()
        cursor.execute(sql_statement, *args)
        result = cursor.fetchall()
        cursor.close()
        return result

    # Language queries
    def languages(self):
        query = "select id from language"
        ids = self._query(query)
        return [Language(self, language_id) for language_id in ids]

    def language_name(self, language, target_language=None):
        query = "select name from language_name "\
                "where src_language=? and dst_language=?"
        names = self._run_query(query, _id(language), _id(target_language))
        return names
        
    # ExpressionClass queries
    def expression_classes(self, language=None):
        query = "select id from expression_class "\
                "where language=?"
        ids = self._query(query, _id(language))
        return [ExpressionClass(self, eclass_id) for eclass_id in ids]

    def expression_class_name(self, eclass, language=None):
        query = "select name from expression_class "\
                "where id=? and language=?"
        names = self._run_query(query, _id(eclass), _id(language))
        return names

    # Function queries
    def functions(self, language=None):
        query = "select id from function "\
                "where language=?"
        ids = self._query(query, _id(language))
        return [Function(self, function_id) for function_id in ids]

    def function_name(self, function, language=None):
        query = "select name from function "\
                "where id=? and language=?"
        names = self._run_query(query, _id(function), _id(language))

    def function_evaluate(self, function, parameter):
        query = "select result from function_eval "\
                "where function=? and parameter=?"
        ids = self._query(query, _id(function), _id(parameter))
        return ids

    # Expression queries
    def expressions(self, language):
        pass

    def expression(self, expression_id, language):
        pass

